package com.feinno.extas.app.util;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantReadWriteLock;

/**<p>
 * 本地锁,使用读写锁实现
 * 使用场景:
 * 1.用于Token验证,并发请求,只需要一个请求到杭研验证,一次验证通过,释放后续所有锁
 * </p>
 */
public class LocalLockUtil {
    private static Logger log = LoggerFactory.getLogger(LocalLockUtil.class);
    private static ConcurrentHashMap<Object, ReentrantReadWriteLock> map = new ConcurrentHashMap<>(8);
    private static ThreadLocal<Lock> localLock = new ThreadLocal();

    /**
     * 加锁
     *
     * @param key
     */
    public static boolean lock(String key) {
        Lock lockLockMe = getLocalLock(key);
        localLock.set(lockLockMe);
        if (lockLockMe instanceof ReentrantReadWriteLock.WriteLock) {
            return true;
        } else {
            lock(key, lockLockMe);
            return false;
        }
    }

    /**
     * 创建锁
     *
     * @param key
     * @return
     */
    private static Lock getLocalLock(String key) {
        ReentrantReadWriteLock lock = map.get(key);
        if (lock == null) {
            synchronized (key) {
                lock = map.get(key);
                if (lock == null) {
                    log.debug("创建:{}", key);
                    lock = new ReentrantReadWriteLock();
                    Lock writeLock = lock.writeLock();
                    lock(key, writeLock);
                    map.put(key, lock);
                    return writeLock;
                }
            }
        }
        return lock.readLock();
    }

    /**
     * 加锁日志
     *
     * @param key
     * @param lock
     */
    private static void lock(Object key, Lock lock) {
        log.debug("KEY:{},LOCK:{},加锁前", key, lock);
        lock.lock();
        log.debug("KEY:{},LOCK:{},加锁后", key, lock);
    }

    /**
     * 解锁
     *
     * @param key
     */
    public static void unlock(Object key) {
        Lock lock = localLock.get();
        log.debug("KEY:{},LOCK:{},解锁", key, lock);
        if (lock == null) {
            return;
        }
        if (lock instanceof ReentrantReadWriteLock.WriteLock) {
            map.remove(key);
        }
        lock.unlock();
        localLock.remove();
    }
}
